#if defined(ARCTIC)
#include "ctc_const.h"
#include "ctc_error.h"
#include "sys_usw_common.h"
#include "sys_usw_wb_common.h"
#include "sys_usw_nexthop_api.h"
#include "sys_usw_nexthop_hw.h"
#include "sys_usw_nexthop.h"
#include "sys_usw_wb_nh.h"
#include "sys_usw_linkagg.h"
#include "sys_usw_parser.h"
#include "sys_usw_ftm.h"
#include "sys_usw_xdata_api.h"
#include "drv_api.h"

#define SYS_NH_NSH_MODE_NONE           0b000
#define SYS_NH_NSH_MODE_ESCL0          0b100
#define SYS_NH_NSH_MODE_ESCL1          0b101
#define SYS_NH_NSH_MODE_ESCL0_PART     0b110
#define SYS_NH_NSH_MODE_ESCL1_PART     0b111

#define SYS_SCL_HASH_TYPE_NSH       0xF
#define SYS_NSH_BH_LEN              2
#define SYS_NSH_INSERT              1
#define SYS_NSH_UPDATE              2
#define SYS_NSH_REMOVE              3
#define SYS_NSH_HASH_BLOCK_SIZE     32

struct sys_nh_pbmp_s
{
     uint8 min_min;/*portid >> 5, means words range*/
     uint8 min_max;/*portid >> 5, means words range*/
     uint8 max_min;/*portid >> 5, means words range*/
     uint8 max_max;/*portid >> 5, means words range*/
     uint8 port_type;
     uint8 entry_type;
};
typedef struct sys_nh_pbmp_s sys_nh_pbmp_t;


extern int32
_sys_usw_nh_mcast_write_dsmet(uint8 lchip, sys_nh_info_dsmet_t* p_met_info, sys_nh_param_mcast_member_t* p_mem_param);
extern int32
_sys_usw_nh_mcast_build_dsmet_info(uint8 lchip, sys_nh_info_mcast_t* p_info_mcast, sys_nh_mcast_dsmet_io_t* p_dsmet_io, sys_nh_info_dsmet_t* p_dsmet);
extern void
sys_usw_nh_dump_mcast_member_info(uint8 lchip, sys_nh_mcast_meminfo_t* p_meminfo_com, uint32* p_memcnt, uint32 gport,uint8 aps_en);
extern int32
_sys_usw_nh_mcast_del_dsmet_entry(uint8 lchip, sys_nh_param_mcast_member_t* p_mem_param,
                                    sys_nh_mcast_meminfo_t* p_mem_info, ctc_list_pointer_t* p_db_member_list, sys_nh_info_mcast_t* p_info_mcast);

int32
sys_at_nh_mcast_map_port_type1(uint8 lchip, uint32 dest_id, uint8* port_type, uint8* min_port_range, uint8* max_port_range, uint8* entry_type, uint32* pbmp)
{
    uint8 i = 0;
    if (pbmp)
    {
        for (i = 0; i < CTC_PORT_BITMAP_IN_WORD; i++)
        {
            if (pbmp[i])
            {
                *min_port_range = i;
                break;
            }
        }

        for (i = CTC_PORT_BITMAP_IN_WORD; i > 0; i--)
        {
            if (pbmp[i - 1])
            {
                *max_port_range = i - 1;
                break;
            }
        }
    }
    else
    {
        uint8 tmp = dest_id >> 5;
        if (tmp <= *min_port_range)
        {
            *min_port_range = tmp;
        }
        if (tmp >= *max_port_range)
        {
            *max_port_range = tmp;
        }
    }
    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "min_port_range = %d, max_port_range = %d\n", *min_port_range, *max_port_range);

    if (g_usw_nh_master[lchip]->met_ext_mode)
    {
/*
        sys_nh_pbmp_t pbmp_db[] =
        {
            {  0, 127,   0, 127, 0, SYS_NH_ENTRY_TYPE_MET_6W},
            {  0, 127, 128, 255, 0, SYS_NH_ENTRY_TYPE_MET_12W},
            {128, 255, 128, 255, 1, SYS_NH_ENTRY_TYPE_MET_6W},
            {256, 383, 256, 383, 2, SYS_NH_ENTRY_TYPE_MET_6W},
            {256, 383, 384, 511, 1, SYS_NH_ENTRY_TYPE_MET_12W},
            {384, 511, 384, 511, 3, SYS_NH_ENTRY_TYPE_MET_6W},
        };

        if (no_need_nh_ptr && (*min_port >= 0) && (*min_port <= 63) && (*max_port >= 0) && (*max_port <= 63))
        {
            *port_type = 0;
            *entry_type = SYS_NH_ENTRY_TYPE_MET;
            return CTC_E_NONE;
        }
        for (i = 0; i < (sizeof(pbmp_db) / sizeof(sys_nh_pbmp_t)); i++)
        {
            if ((*min_port >= pbmp_db[i].min_min) && (*min_port <= pbmp_db[i].min_max)
                && (*max_port >= pbmp_db[i].max_min) && (*max_port <= pbmp_db[i].max_max))
            {
                *port_type = pbmp_db[i].port_type;
                *entry_type = pbmp_db[i].entry_type;
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "i:%d, port_type:%d, entry_type:%d\n", i, *port_type, *entry_type);
                return CTC_E_NONE;
            }
        }*/
    }
    else
    {
        sys_nh_pbmp_t pbmp_db[] =
        {
            { 0,  2,  0,  2, 0, SYS_NH_ENTRY_TYPE_MET},
            { 0,  2,  3,  5, 0, SYS_NH_ENTRY_TYPE_MET_6W},
            { 0,  2,  6, 15, 0, SYS_NH_ENTRY_TYPE_MET_12W},
            { 3,  5,  3,  5, 1, SYS_NH_ENTRY_TYPE_MET},
            { 3,  5,  6,  8, 1, SYS_NH_ENTRY_TYPE_MET_6W},
            { 3,  5,  9, 15, 0, SYS_NH_ENTRY_TYPE_MET_12W},
            { 6,  8,  6,  8, 2, SYS_NH_ENTRY_TYPE_MET},
            { 6,  8,  9, 11, 2, SYS_NH_ENTRY_TYPE_MET_6W},
            { 6,  8, 12, 15, 0, SYS_NH_ENTRY_TYPE_MET_12W},
            { 9, 11,  9, 11, 3, SYS_NH_ENTRY_TYPE_MET},
            { 9, 11, 12, 15, 3, SYS_NH_ENTRY_TYPE_MET_6W},
            {12, 15, 12, 15, 3, SYS_NH_ENTRY_TYPE_MET_6W},
            { 0, 15,  0, 15, 0, SYS_NH_ENTRY_TYPE_MET_12W},
        };

        for (i = 0; i < (sizeof(pbmp_db) / sizeof(sys_nh_pbmp_t)); i++)
        {
            if ((*min_port_range >= pbmp_db[i].min_min) && (*min_port_range <= pbmp_db[i].min_max)
                && (*max_port_range >= pbmp_db[i].max_min) && (*max_port_range <= pbmp_db[i].max_max))
            {
                *port_type = pbmp_db[i].port_type;
                *entry_type = pbmp_db[i].entry_type;
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "i:%d, port_type:%d, entry_type:%d\n", i, *port_type, *entry_type);
                return CTC_E_NONE;
            }
        }
    }

    return CTC_E_NOT_EXIST;
}

int32
sys_at_nh_mcast_analyze_member(uint8 lchip, ctc_list_pointer_t* p_db_member_list,
                                       sys_nh_param_mcast_member_t* p_member,
                                       sys_nh_mcast_meminfo_t** pp_mem_node,
                                       bool* p_entry_exit)
{

    sys_nh_mcast_meminfo_t* p_meminfo;
    ctc_list_pointer_node_t* p_pos_mem;
    uint16 hw_ldp_ext = 0;
    uint8 refect_en = 0;

    *pp_mem_node = NULL;
    *p_entry_exit = FALSE;
    if ((p_member->member_type != SYS_NH_PARAM_MCAST_MEM_LOCAL_WITH_NH) &&
        (p_member->member_type != SYS_NH_PARAM_BRGMC_MEM_LOCAL_WITH_APS_BRIDGE)
        && (0 == p_member->logic_port))
    {
        p_member->is_logic_port = 0;
    }

    refect_en = (g_usw_nh_master[lchip]->reflective_brg_en || p_member->is_src_port_check_dis);
    CTC_LIST_POINTER_LOOP(p_pos_mem, p_db_member_list)
    {
        p_meminfo = _ctc_container_of(p_pos_mem, sys_nh_mcast_meminfo_t, list_head);
        if (p_meminfo->dsmet.member_type != p_member->member_type)
        {
            if((((SYS_NH_PARAM_BRGMC_MEM_LOCAL_WITH_APS_BRIDGE == p_meminfo->dsmet.member_type)||(SYS_NH_PARAM_BRGMC_MEM_LOCAL_WITH_APS_BRIDGE == p_member->member_type))
                &&(p_meminfo->dsmet.ref_nhid == p_member->ref_nhid))  || ((SYS_NH_PARAM_BRGMC_MEM_LOCAL == p_meminfo->dsmet.member_type ||
                SYS_NH_PARAM_BRGMC_MEM_BITMAP ==  p_meminfo->dsmet.member_type) && (SYS_NH_PARAM_BRGMC_MEM_LOCAL == p_member->member_type ||
                SYS_NH_PARAM_BRGMC_MEM_BITMAP ==  p_member->member_type)&& p_member->is_del))
            {

            }
            else
            {
                continue;
            }
        }

        hw_ldp_ext = 0;
        if (CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM) ||
            CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_LDP_EXT_EN))
        {
            sys_met_t dsmet;
            sal_memset(&dsmet, 0, sizeof(dsmet));
            CTC_ERROR_RETURN(sys_usw_nh_read_asic_table(lchip, p_meminfo->dsmet.entry_type, p_meminfo->dsmet.dsmet_offset, &dsmet));
            sal_memcpy(p_member->pbm_hw, dsmet.port_bitmap, MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM) >> 3);
            hw_ldp_ext = dsmet.ldp_ext;
        }

         switch (p_member->member_type)
         {
         case  SYS_NH_PARAM_BRGMC_MEM_LOCAL_WITH_APS_BRIDGE:
         case  SYS_NH_PARAM_MCAST_MEM_LOCAL_WITH_NH:
         case  SYS_NH_PARAM_IPMC_MEM_LOOP_BACK:
             if ((p_meminfo->dsmet.ref_nhid == p_member->ref_nhid) && (p_meminfo->dsmet.fid == p_member->fid))
             {
                 *pp_mem_node = p_meminfo;
                 *p_entry_exit = TRUE;
                 return CTC_E_NONE;
             }
             break;
         case SYS_NH_PARAM_MCAST_MEM_LOCAL_WITH_NH_DESTID:
            if (!p_member->is_logic_port && !p_member->ldp_ext && CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM)
                && CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG) == p_member->is_linkagg
                && (SYS_MC_DESTID_IS_NOT_TOCPU(p_member->destid))
                && (p_meminfo->dsmet.ref_nhid == p_member->ref_nhid)
                && (p_meminfo->dsmet.fid == p_member->fid)
                && ((refect_en == !CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_PORT_CHK_DISCARD)) || p_member->is_del))
            {
                if (!p_member->is_del)
                {
                    *pp_mem_node = p_meminfo;
                    *p_entry_exit = CTC_BMP_ISSET(p_member->pbm_hw, p_member->destid);
                    return CTC_E_NONE;
                }
                else if (CTC_BMP_ISSET(p_member->pbm_hw, p_member->destid))
                {
                    *pp_mem_node = p_meminfo;
                    *p_entry_exit = TRUE;
                    return CTC_E_NONE;
                }
            }
            else if (
                CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG) == p_member->is_linkagg
                && (p_meminfo->dsmet.ucastid == p_member->destid)
                && (p_meminfo->dsmet.ref_nhid == p_member->ref_nhid)
                && (hw_ldp_ext == p_member->ldp_ext)
                && ((refect_en == !CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_PORT_CHK_DISCARD)) ||  p_member->is_del)
                && !CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_LOGIC_REPLI_EN)
                && !CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM))
            {
                *pp_mem_node = p_meminfo;
                *p_entry_exit = TRUE;
                return CTC_E_NONE;
            }
            break;

        case SYS_NH_PARAM_MCAST_MEM_MIRROR_WITH_NH:
            if (p_meminfo->dsmet.ref_nhid == p_member->ref_nhid)
            {
                *pp_mem_node = p_meminfo;
                *p_entry_exit = TRUE;
                return CTC_E_NONE;
            }

            break;

        case SYS_NH_PARAM_BRGMC_MEM_LOCAL:
        case SYS_NH_PARAM_BRGMC_MEM_RAPS:
            if (!p_member->is_logic_port && CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM)
                && (CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG) == p_member->is_linkagg)
            && (SYS_MC_DESTID_IS_NOT_TOCPU(p_member->destid))
            && ((refect_en == !CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_PORT_CHK_DISCARD)) || p_member->is_del))
            {
                if (!p_member->is_del)
                {
                     *pp_mem_node = p_meminfo;
                     *p_entry_exit = CTC_BMP_ISSET(p_member->pbm_hw, p_member->destid);
                     return CTC_E_NONE;
                }
                else if (CTC_BMP_ISSET(p_member->pbm_hw, p_member->destid))
                {
                    *pp_mem_node = p_meminfo;
                    *p_entry_exit = TRUE;
                    return CTC_E_NONE;
                }
            }
            else if (
                CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG) == p_member->is_linkagg &&
                p_meminfo->dsmet.ucastid == p_member->destid &&
                CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_LOGIC_PORT_CHK) == p_member->is_logic_port &&
                p_meminfo->dsmet.logic_port == p_member->logic_port &&
                ((refect_en == !CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_PORT_CHK_DISCARD)) || p_member->is_del))
            {
                *pp_mem_node = p_meminfo;
                *p_entry_exit = TRUE;
                return CTC_E_NONE;
            }
            break;
       case SYS_NH_PARAM_BRGMC_MEM_BITMAP:
           if ( !p_member->is_logic_port && !p_member->is_linkagg && CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM)
                && (!CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG) ))
            { /*only suport port bitmap */
                 *pp_mem_node = p_meminfo;
                 *p_entry_exit = TRUE;
                 return CTC_E_NONE;
            }
          break;
       case SYS_NH_PARAM_MCAST_MEM_REMOTE:

            if ((CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG) == p_member->is_linkagg) &&
                (p_meminfo->dsmet.ucastid == p_member->destid))
            {
                *pp_mem_node = p_meminfo;
                *p_entry_exit = TRUE;
                return CTC_E_NONE;
            }

            break;

        case  SYS_NH_PARAM_IPMC_MEM_LOCAL:
                if (CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM)
                && (p_member->l3if_type == CTC_L3IF_TYPE_VLAN_IF)
                && (p_meminfo->dsmet.vid == p_member->vid)
                && CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG) == p_member->is_linkagg
                && (p_member->leaf_check_en == CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_LEAF_CHECK_EN)))
            {
                 *pp_mem_node = p_meminfo;
                 *p_entry_exit = CTC_BMP_ISSET(p_member->pbm_hw, p_member->destid);
                 return CTC_E_NONE;
            }
            else
            {
                if ((CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG) == p_member->is_linkagg)
                    && (p_meminfo->dsmet.ucastid == p_member->destid)
                    && (p_meminfo->dsmet.vid == p_member->vid)
                    && (p_meminfo->dsmet.cvid == p_member->cvid)
                    && (p_member->leaf_check_en == CTC_FLAG_ISSET(p_meminfo->dsmet.flag, SYS_NH_DSMET_FLAG_LEAF_CHECK_EN)))
                {
                    *pp_mem_node = p_meminfo;
                    *p_entry_exit = TRUE;
                    return CTC_E_NONE;
                }
            }
            break;
        default:
            break;
        }
    }


    return CTC_E_NONE;
}

int32
sys_at_nh_mcast_add_dsmet_entry(uint8 lchip, void* mem_param,
                                        uint32 dsnh_offset,
                                        uint8 use_dsnh8w,
                                        void* db_member_list,
                                        void* info_mcast)
{
    int32 ret = CTC_E_NONE;
    sys_nh_param_mcast_member_t* p_mem_param = (sys_nh_param_mcast_member_t*)mem_param;
    ctc_list_pointer_t* p_db_member_list = (ctc_list_pointer_t*)db_member_list;
    sys_nh_info_mcast_t* p_info_mcast = (sys_nh_info_mcast_t*)info_mcast;
    sys_nh_mcast_meminfo_t* p_mem_flex = NULL;
    uint32 new_met_offset = 0;
    sys_nh_mcast_dsmet_io_t dsmet_io;
    sys_nh_mcast_meminfo_t* p_member_info;
    uint32 first_met_offset = 0;
    //uint8 use_6w = 0;
    uint8 basic_met_ability = 0;
    uint8 entry_type = SYS_NH_ENTRY_TYPE_MET;
    uint8 min_port_range = 0xf;
    uint8 max_port_range = 0;

    if (p_mem_param->no_hw)
    {
        p_member_info = mem_malloc(MEM_NEXTHOP_MODULE, sizeof(sys_nh_mcast_meminfo_param_t));
    }
    else
    {
        p_member_info = mem_malloc(MEM_NEXTHOP_MODULE, sizeof(sys_nh_mcast_meminfo_t));
    }

    if (!p_member_info)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory \n");
        return CTC_E_NO_MEMORY;
    }

    sal_memset(p_member_info, 0, sizeof(sys_nh_mcast_meminfo_t));
    p_member_info->dsmet.flag |= use_dsnh8w ? SYS_NH_DSMET_FLAG_USE_DSNH8W : 0;
    p_member_info->dsmet.entry_type = SYS_NH_ENTRY_TYPE_MET;
    p_member_info->dsmet.fid = p_mem_param->fid;
    /*1. Init param*/
    sal_memset(&dsmet_io, 0, sizeof(sys_nh_mcast_dsmet_io_t));
    dsmet_io.p_mem_param = p_mem_param;

    if ((SYS_NH_PARAM_BRGMC_MEM_LOCAL == p_mem_param->member_type ||
        SYS_NH_PARAM_BRGMC_MEM_RAPS == p_mem_param->member_type ||
        SYS_NH_PARAM_MCAST_MEM_LOCAL_WITH_NH_DESTID == p_mem_param->member_type ||
    (SYS_NH_PARAM_IPMC_MEM_LOCAL == p_mem_param->member_type &&
    p_mem_param->l3if_type == CTC_L3IF_TYPE_VLAN_IF)  )
    && !p_mem_param->is_logic_port
    && !p_mem_param->leaf_check_en
    && (SYS_MC_DESTID_IS_NOT_TOCPU(p_mem_param->destid)))
    {
        uint8 port_type = 0;
        sys_at_nh_mcast_map_port_type1(lchip, p_mem_param->destid, &port_type, &min_port_range, &max_port_range, &entry_type, NULL);
        p_member_info->dsmet.port_type = port_type;
        p_member_info->dsmet.min_port_range = min_port_range;
        p_member_info->dsmet.max_port_range = max_port_range;
        p_member_info->dsmet.vid = p_mem_param->vid;
        p_member_info->dsmet.ucastid = p_mem_param->destid;
        p_member_info->dsmet.replicate_num  = 0;

        /*reflective member only used for new added members,and reflective member only be used in linked lists*/
        if(!p_mem_param->is_src_port_check_dis)
        {
            CTC_SET_FLAG(p_member_info->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM);
            /*new met usw port bitmap, pass pbmp to write dsmet entry*/
            sal_memset(p_mem_param->pbm_hw, 0, sizeof(p_mem_param->pbm_hw));
            CTC_BMP_SET(p_mem_param->pbm_hw, p_mem_param->destid);

            if (p_mem_param->no_hw)
            {
                sys_nh_mcast_meminfo_param_t* mem_param_db = (sys_nh_mcast_meminfo_param_t*)p_member_info;
                sal_memset(mem_param_db->member.pbm_hw, 0, sizeof(p_mem_param->pbm_hw));
                CTC_BMP_SET(mem_param_db->member.pbm_hw, p_mem_param->destid); 
            }
        }
    }
    else if (SYS_NH_PARAM_BRGMC_MEM_BITMAP == p_mem_param->member_type)
    {
          int32 count = 0;
          uint8 port_type = 0;
          sal_memcpy(p_mem_param->pbm_hw, p_mem_param->pbm, MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM) >> 3);
          CTC_BMP_COUNT_RANGE(p_mem_param->pbm,count, 0, MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM));
          p_member_info->dsmet.replicate_num = count-1;
          p_info_mcast->physical_replication_num += count;
          sys_at_nh_mcast_map_port_type1(lchip, p_mem_param->destid, &port_type, &min_port_range, &max_port_range, &entry_type, p_mem_param->pbm_hw);
          p_member_info->dsmet.port_type = port_type;
          p_member_info->dsmet.min_port_range = min_port_range;
          p_member_info->dsmet.max_port_range = max_port_range;
          CTC_SET_FLAG(p_member_info->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM);
    }

    if (g_usw_nh_master[lchip]->met_ext_mode)
    {
        if ((SYS_MET_VEC_SLIM_1X == p_member_info->dsmet.port_type) &&/* DsMetVectorSlim1X have not nhptr, at least use DsMetVectorSlim2X*/
            ((SYS_NH_PARAM_IPMC_MEM_LOCAL == p_mem_param->member_type &&
        p_mem_param->l3if_type == CTC_L3IF_TYPE_VLAN_IF)
        || (SYS_NH_PARAM_MCAST_MEM_LOCAL_WITH_NH_DESTID == p_mem_param->member_type)))
        {
            //use_6w = 1;
            p_member_info->dsmet.entry_type = SYS_NH_ENTRY_TYPE_MET_6W;
        }
        else
        {
            p_member_info->dsmet.entry_type = (p_member_info->dsmet.port_type >= SYS_MET_VEC_SLIM_2X_0)? SYS_NH_ENTRY_TYPE_MET_6W : SYS_NH_ENTRY_TYPE_MET;
            //use_6w = (p_member_info->dsmet.port_type >= SYS_MET_VEC_SLIM_2X_0)? 1 : 0;
        }
    }
    else
    {
        p_member_info->dsmet.entry_type = entry_type;
    }

    if ((entry_type == SYS_NH_ENTRY_TYPE_MET)
        || ((entry_type == SYS_NH_ENTRY_TYPE_MET_6W) && (CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_2X_GRP_ID)
    || CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_4X_GRP_ID)))
    || ((entry_type == SYS_NH_ENTRY_TYPE_MET_12W) && (CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_4X_GRP_ID))))
    {
        basic_met_ability = 1;
    }

    /*2. Build new allocate member, and update flex member(prev member or next member)*/
    /*2.1 New member is the list's first member*/
    if (ctc_list_pointer_empty(p_db_member_list))
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "Mcast list is empty!\n");
        dsmet_io.met_offset = p_info_mcast->basic_met_offset;
        first_met_offset = p_info_mcast->basic_met_offset;

        if (0 == basic_met_ability)
        {
            sys_nh_info_dsmet_t dsmet;
            sal_memset(&dsmet, 0, sizeof(sys_nh_info_dsmet_t));
            ret = (sys_usw_nh_offset_alloc(lchip, p_member_info->dsmet.entry_type, p_mem_param->no_hw? 0 : 1, &new_met_offset));
            if (ret)
            {
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "Alloc met offset failed!\n");
                goto error1;
            }

            dsmet.dsmet_offset = p_info_mcast->basic_met_offset;
            dsmet.next_dsmet_offset = new_met_offset;
            dsmet.entry_type = SYS_NH_ENTRY_TYPE_MET;
            dsmet.ucastid = SYS_NH_MET_DROP_UCAST_ID;
            ret = (_sys_usw_nh_mcast_write_dsmet(lchip, &dsmet, p_mem_param));
            if (ret)
            {
                sys_usw_nh_offset_free(lchip, p_member_info->dsmet.entry_type, 1, new_met_offset);
                goto error1;
            }
            CTC_SET_FLAG(p_member_info->dsmet.flag, SYS_NH_DSMET_FLAG_APPEND_DROP);
            dsmet_io.met_offset = new_met_offset;
            first_met_offset = new_met_offset;
        }

        if (CTC_FLAG_ISSET(p_member_info->dsmet.flag, SYS_NH_DSMET_FLAG_IS_MCAST_APS))
        {
            ret = (sys_usw_nh_offset_alloc(lchip, SYS_NH_ENTRY_TYPE_MET, p_mem_param->no_hw? 0 : 1, &new_met_offset));
            if (ret)
            {
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "Alloc met offset failed!\n");
                goto error1;
            }
            p_member_info->dsmet.dsmet_offset = new_met_offset;
            dsmet_io.met_offset = new_met_offset;
            CTC_SET_FLAG(p_member_info->dsmet.flag, SYS_NH_DSMET_FLAG_IS_BASIC_MET);
        }

        dsmet_io.dsnh_offset = dsnh_offset;
        dsmet_io.p_next_mem = NULL;
        CTC_ERROR_GOTO(_sys_usw_nh_mcast_build_dsmet_info(lchip, p_info_mcast, &dsmet_io, &(p_member_info->dsmet)), ret, error1);

        /*Add this new member to db member list*/
        ctc_list_pointer_insert_head(p_db_member_list, &(p_member_info->list_head));


        ret = _sys_usw_nh_mcast_write_dsmet(lchip,&(p_member_info->dsmet), p_mem_param);
        if (ret)
        {
            ctc_list_pointer_delete_head(p_db_member_list);
            goto error1;
        }
        if(first_met_offset == p_info_mcast->basic_met_offset)
        {
            CTC_SET_FLAG(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_BASC_OFFSET_USED);
        }
    }
    else
    {
       if(p_member_info->dsmet.entry_type == SYS_NH_ENTRY_TYPE_MET
        && !CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_BASC_OFFSET_USED)
        && !CTC_FLAG_ISSET(p_member_info->dsmet.flag, SYS_NH_DSMET_FLAG_IS_MCAST_APS))
       {
          new_met_offset = p_info_mcast->basic_met_offset;
       }
      else
      {
        /*2.2 New member is not the list's first member*/
        ret = (sys_usw_nh_offset_alloc(lchip, p_member_info->dsmet.entry_type, p_mem_param->no_hw? 0 : (p_mem_param->is_mcast_aps? 2 : 1), &new_met_offset));
          if (ret)
           {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "Alloc met offset failed!\n");

            goto error1;
          }
       }

        /*(1) insert new member into tail*/
        dsmet_io.met_offset = new_met_offset;
        dsmet_io.dsnh_offset = dsnh_offset;
        dsmet_io.p_next_mem = NULL;
        ret = (_sys_usw_nh_mcast_build_dsmet_info(lchip, p_info_mcast, &dsmet_io, &(p_member_info->dsmet)));
        if (ret)
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "Build met information failed!\n");
            goto error2;
        }

       if(p_member_info->dsmet.dsmet_offset == p_info_mcast->basic_met_offset)
       {
            /*The list have one member at least, so the tail node should not be NULL*/
             p_mem_flex = _ctc_container_of(ctc_list_pointer_head(p_db_member_list),
                                        sys_nh_mcast_meminfo_t, list_head);
             p_member_info->dsmet.next_dsmet_offset = p_mem_flex->dsmet.dsmet_offset;

          /*Add this new member to db member list*/
          ctc_list_pointer_insert_head(p_db_member_list, &(p_member_info->list_head));
          CTC_ERROR_GOTO(_sys_usw_nh_mcast_write_dsmet(lchip,&(p_member_info->dsmet), p_mem_param), ret, error3);
          CTC_SET_FLAG(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_BASC_OFFSET_USED);

      }
     else
     {
           /*The list have one member at least, so the tail node should not be NULL*/
          p_mem_flex = _ctc_container_of(ctc_list_pointer_node_tail(p_db_member_list),
                                        sys_nh_mcast_meminfo_t, list_head);

           /*Add this new member to db member list*/
          ctc_list_pointer_insert_tail(p_db_member_list, &(p_member_info->list_head));
          CTC_ERROR_GOTO(_sys_usw_nh_mcast_write_dsmet(lchip,&(p_member_info->dsmet), p_mem_param), ret, error3);

           /*(2) update previous member's next met offset*/
           p_mem_flex->dsmet.next_dsmet_offset = p_member_info->dsmet.dsmet_offset;
          CTC_UNSET_FLAG(p_mem_flex->dsmet.flag, SYS_NH_DSMET_FLAG_ENDLOCAL);
          /*write previous met, need to recover port bmp or ldp ext from hw*/
          if(CTC_FLAG_ISSET(p_mem_flex->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM) ||
            CTC_FLAG_ISSET(p_mem_flex->dsmet.flag, SYS_NH_DSMET_FLAG_LDP_EXT_EN))
          {
              sys_met_t dsmet;
              sal_memset(&dsmet, 0, sizeof(dsmet));
              CTC_ERROR_RETURN(sys_usw_nh_read_asic_table(lchip, p_mem_flex->dsmet.entry_type, p_mem_flex->dsmet.dsmet_offset, &dsmet));
              sal_memcpy(p_mem_param->pbm_hw, dsmet.port_bitmap, (MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM) >> 3));
              p_mem_param->ldp_ext = dsmet.ldp_ext;
          }
          CTC_ERROR_GOTO(_sys_usw_nh_mcast_write_dsmet(lchip, &(p_mem_flex->dsmet), p_mem_param),ret, error3);
     }

    }

    return CTC_E_NONE;

error3:
    if(p_member_info->dsmet.dsmet_offset == p_info_mcast->basic_met_offset)
    {
       ctc_list_pointer_delete_head(p_db_member_list);
    }
    else
    {
        ctc_list_pointer_delete(p_db_member_list, &(p_member_info->list_head));
    }
error2:
    sys_usw_nh_offset_free(lchip, p_member_info->dsmet.entry_type, 1, p_member_info->dsmet.dsmet_offset);
error1:
    mem_free(p_member_info);
    return ret;
}

int32
sys_at_nh_mcast_update_mem(uint8 lchip, sys_nh_param_mcast_member_t* p_mem_param,
                            ctc_list_pointer_t* p_db_member_list,
                            sys_nh_info_mcast_t* p_info_mcast, sys_nh_mcast_meminfo_t* p_member_info, uint8 is_add)
{
    int32 ret = CTC_E_NONE;
    uint32 new_met_offset = 0;
    uint32 old_met_offset = 0;
    uint8 old_entry_type = 0;
    uint16 old_min_port_range = 0;
    uint16 old_max_port_range = 0;
    uint8 old_port_type = 0;
    uint8 move_to_basic = 0;
    uint8 basic_met_ability = 0;
    uint8 port_type = 0;
    int32 delta_count = 0;
    uint8 entry_type = SYS_NH_ENTRY_TYPE_MET;
    uint8 min_port_range = p_member_info->dsmet.min_port_range;
    uint8 max_port_range = p_member_info->dsmet.max_port_range;

    old_met_offset = p_member_info->dsmet.dsmet_offset;
    old_entry_type = p_member_info->dsmet.entry_type;
    old_min_port_range = p_member_info->dsmet.min_port_range;
    old_max_port_range = p_member_info->dsmet.max_port_range;
    old_port_type = p_member_info->dsmet.port_type;
    /*1. cale pbmp*/
    if (p_mem_param->member_type == SYS_NH_PARAM_BRGMC_MEM_BITMAP)
    {
        uint32 actual_bpm[CTC_PORT_BITMAP_IN_WORD] = {0};
        uint8 loop = 0;
        if(is_add)
        {
            while (loop < (MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM) / 32))
            {
                actual_bpm[loop] = (~(p_mem_param->pbm_hw[loop]))  & p_mem_param->pbm[loop];
                p_mem_param->pbm_hw[loop]  |= p_mem_param->pbm[loop];
                loop++;
            }
            CTC_BMP_COUNT_RANGE(actual_bpm, delta_count, 0, MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM));
            p_member_info->dsmet.replicate_num += delta_count;
            p_info_mcast->physical_replication_num += delta_count;
        }
        else
        {
            while(loop < (MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM)/32))
            {
                 actual_bpm[loop] = p_mem_param->pbm_hw[loop] & p_mem_param->pbm[loop];
                 p_mem_param->pbm_hw[loop]  &= (~actual_bpm[loop]);
                 loop++;
            }
            CTC_BMP_COUNT_RANGE(actual_bpm,delta_count, 0,MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM));

            p_info_mcast->physical_replication_num -= delta_count;
            if (p_member_info->dsmet.replicate_num >= delta_count)
            {
                p_member_info->dsmet.replicate_num -= delta_count;
            }
            else
            {
                /*Remove member node*/
                CTC_ERROR_RETURN(_sys_usw_nh_mcast_del_dsmet_entry(lchip, p_mem_param,
                                                                   p_member_info,
                                                                   p_db_member_list,
                                                                   p_info_mcast));
                return CTC_E_NONE;
            }
        }
        sys_at_nh_mcast_map_port_type1(lchip, p_mem_param->destid, &port_type, &min_port_range, &max_port_range, &entry_type, p_mem_param->pbm_hw);
    }
    else if (CTC_FLAG_ISSET(p_member_info->dsmet.flag, SYS_NH_DSMET_FLAG_USE_PBM))
    {
        sys_nh_mcast_meminfo_param_t* mem_param = NULL;
        p_member_info->dsmet.ucastid = 0;
        if (is_add)
        {
            CTC_BMP_SET(p_mem_param->pbm_hw, p_mem_param->destid);
            p_member_info->dsmet.replicate_num += 1;
            p_info_mcast->physical_replication_num += 1;
            if (p_mem_param->no_hw)
            {
                mem_param = (sys_nh_mcast_meminfo_param_t*)p_member_info;
                CTC_BMP_SET(mem_param->member.pbm_hw, p_mem_param->destid); 
            }            
        }
        else
        {
            CTC_BMP_UNSET(p_mem_param->pbm_hw, p_mem_param->destid);
            p_member_info->dsmet.replicate_num -= 1;
            p_info_mcast->physical_replication_num -= 1;
        }
        sys_at_nh_mcast_map_port_type1(lchip, p_mem_param->destid, &port_type, &min_port_range, &max_port_range, &entry_type, p_mem_param->no_hw?mem_param->member.pbm_hw:p_mem_param->pbm_hw);
    }

    /*2. cale ability*/
    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "old: port_type:%d, entry_type:%d\n", p_member_info->dsmet.port_type, p_member_info->dsmet.entry_type);
    if ((port_type != p_member_info->dsmet.port_type)
        ||(entry_type != p_member_info->dsmet.entry_type))/*alloc new met for update to bigger or smaller*/
    {
        if ((entry_type == SYS_NH_ENTRY_TYPE_MET)
            || ((entry_type == SYS_NH_ENTRY_TYPE_MET_6W) && (CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_2X_GRP_ID)
                                                           ||CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_4X_GRP_ID)))
        || ((entry_type == SYS_NH_ENTRY_TYPE_MET_12W) && (CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_4X_GRP_ID))))
        {
            basic_met_ability = 1;
        }

        if ((old_met_offset == p_info_mcast->basic_met_offset) && basic_met_ability)/*target is basic met*/
        {
            /*update basic met*/
        }
        else if ((ctc_list_pointer_head(p_db_member_list) == (&(p_member_info->list_head))) && /*target is the first node*/
            (!CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_BASC_OFFSET_USED)) && basic_met_ability)/* basic met unused*/
        {
            move_to_basic = 1;
        }
        else
        {
            CTC_ERROR_GOTO(sys_usw_nh_offset_alloc(lchip, entry_type, p_mem_param->no_hw? 0 : 1, &new_met_offset), ret, error1);
        }
        p_member_info->dsmet.port_type = port_type;
        p_member_info->dsmet.entry_type = entry_type;
        p_member_info->dsmet.min_port_range = min_port_range;
        p_member_info->dsmet.max_port_range = max_port_range;
    }

    if (NULL == p_member_info->list_head.p_next
        && !CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_STK_MEM_PROFILE)
    && !CTC_FLAG_ISSET(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_MEM_PROFILE))
    {
        if ( p_info_mcast->profile_met_offset)
        {
            p_member_info->dsmet.next_dsmet_offset = p_info_mcast->profile_met_offset;
        }
        else
        {
            p_member_info->dsmet.next_dsmet_offset = SYS_NH_MET_END_REPLICATE_OFFSET;
        }
    }

    /*3. write dsmet*/
    if (0 == new_met_offset)
    {
        p_member_info->dsmet.dsmet_offset = move_to_basic? p_info_mcast->basic_met_offset : p_member_info->dsmet.dsmet_offset;
        CTC_ERROR_RETURN(_sys_usw_nh_mcast_write_dsmet(lchip, &p_member_info->dsmet, p_mem_param));
        if (move_to_basic)/*first node and basic is unused*/
        {
            CTC_SET_FLAG(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_BASC_OFFSET_USED);
            sys_usw_nh_offset_free(lchip, old_entry_type, p_mem_param->no_hw? 0 : 1, old_met_offset);
        }
    }
    else/*write new met*/
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "Write new met, new_met_offset:%d\n", new_met_offset);
        /*1. write new*/
        p_member_info->dsmet.dsmet_offset = new_met_offset;
        CTC_ERROR_GOTO(_sys_usw_nh_mcast_write_dsmet(lchip, &p_member_info->dsmet, p_mem_param), ret, error2);

        /*2. update pre node*/
        if ((old_met_offset == p_info_mcast->basic_met_offset)/*Target member is the basic met*/
            ||((ctc_list_pointer_head(p_db_member_list) == (&(p_member_info->list_head)))))/*Target member is first node*/
        {
            sys_nh_info_dsmet_t dsmet;
            sal_memset(&dsmet, 0, sizeof(sys_nh_info_dsmet_t));
            /*update basic to drop*/
            dsmet.dsmet_offset = p_info_mcast->basic_met_offset;
            dsmet.next_dsmet_offset = new_met_offset;
            dsmet.entry_type = SYS_NH_ENTRY_TYPE_MET;
            dsmet.ucastid = SYS_NH_MET_DROP_UCAST_ID;
            CTC_ERROR_GOTO(_sys_usw_nh_mcast_write_dsmet(lchip, &dsmet, p_mem_param), ret, error2);
            if (old_met_offset == p_info_mcast->basic_met_offset)
            {
                CTC_UNSET_FLAG(p_info_mcast->mcast_flag, SYS_NH_INFO_MCAST_FLAG_BASC_OFFSET_USED);
            }
        }
        else
        {
            sys_nh_mcast_meminfo_t* p_mem_flex = NULL;
            sys_met_t dsmet_hw;
            sal_memset(&dsmet_hw, 0, sizeof(dsmet_hw));
            p_mem_flex = _ctc_container_of(p_member_info->list_head.p_prev, sys_nh_mcast_meminfo_t, list_head);
            CTC_ERROR_GOTO(sys_usw_nh_read_asic_table(lchip, p_mem_flex->dsmet.entry_type, p_mem_flex->dsmet.dsmet_offset, &dsmet_hw), ret, error2);
            sal_memcpy(p_mem_param->pbm_hw, dsmet_hw.port_bitmap, sizeof(dsmet_hw.port_bitmap));
            p_mem_flex->dsmet.next_dsmet_offset = new_met_offset;
            CTC_ERROR_GOTO(_sys_usw_nh_mcast_write_dsmet(lchip, &p_mem_flex->dsmet, p_mem_param), ret, error2);
        }
        /*3. free old*/
        if (old_met_offset !=  p_info_mcast->basic_met_offset)
        {
            sys_usw_nh_offset_free(lchip, old_entry_type, p_mem_param->no_hw? 0 : 1, old_met_offset);
        }
    }

    return CTC_E_NONE;
error2:
    if(new_met_offset)
    {
        sys_usw_nh_offset_free(lchip, old_entry_type, p_mem_param->no_hw? 0 : 1, new_met_offset);
    }

error1:
    p_member_info->dsmet.port_type = old_port_type;
    p_member_info->dsmet.entry_type = old_entry_type;
    p_member_info->dsmet.min_port_range = old_min_port_range;
    p_member_info->dsmet.max_port_range = old_max_port_range;
    if (p_mem_param->member_type == SYS_NH_PARAM_BRGMC_MEM_BITMAP)
    {
        if (is_add)
        {
            p_member_info->dsmet.replicate_num -= delta_count;
            p_info_mcast->physical_replication_num -= delta_count;
        }
        else
        {
            p_member_info->dsmet.replicate_num += delta_count;
            p_info_mcast->physical_replication_num += delta_count;
        }
    }

    return ret;
}

void
sys_at_nh_dump_mcast_bitmap_info(uint8 lchip, void* p_meminfo, uint8 gchip, uint32* p_memcnt, sys_met_t* p_dsmet)
{
    uint16 gport = 0;
    uint32 lport = 0;
    sys_nh_mcast_meminfo_t* p_meminfo_com = (sys_nh_mcast_meminfo_t*)p_meminfo;
    for (lport = 0; lport < MCHIP_CAP(SYS_CAP_NH_MC_PBMP_NUM); lport++)
    {
        if (CTC_BMP_ISSET(p_dsmet->port_bitmap, lport))
        {
            if (CTC_FLAG_ISSET(p_meminfo_com->dsmet.flag, SYS_NH_DSMET_FLAG_IS_LINKAGG))
            {
                gport = CTC_MAP_TID_TO_GPORT(lport);
            }
            else
            {
                gport = CTC_MAP_LPORT_TO_GPORT(gchip, lport);
            }
            (*p_memcnt)++;
            sys_usw_nh_dump_mcast_member_info(lchip, p_meminfo_com, p_memcnt, gport, 0);
        }
    }
}

#define ____NSH_START____

#define SYS_NH_NSH_PROF_INVALID     0
#define SYS_NH_NSH_PROF_VXLAN_RSV   1
#define SYS_NH_NSH_PROF_USER        2

#define SYS_NH_NSH_SET_HW_CH(nsh_ch)  \
    {\
        uint8 _loop_ = 0;\
        uint8 _temp_ = 0;\
        do {\
            _temp_ = (nsh_ch)[_loop_];\
            (nsh_ch)[_loop_] = (nsh_ch)[CTC_NH_NSH_CH_LEN - 1 - _loop_];\
            (nsh_ch)[CTC_NH_NSH_CH_LEN - 1 - _loop_] = _temp_;\
        }while(++_loop_ < (CTC_NH_NSH_CH_LEN>>1));\
    }

STATIC uint32
_sys_at_nh_nsh_hash_make(sys_nh_db_nsh_t* p_node)
{
    return p_node->nsh_id;
}

STATIC bool
_sys_at_nh_nsh_hash_cmp(sys_nh_db_nsh_t* p_node1, sys_nh_db_nsh_t* p_node2)
{
    return (p_node1->nsh_id == p_node2->nsh_id);
}

STATIC int32
_sys_at_nh_nsh_free_node(sys_nh_db_nsh_t* p_node, void* user_data)
{
    if (p_node)
    {
        mem_free(p_node);
    }
    return CTC_E_NONE;
}

void
sys_at_nh_build_nsh_edit(uint8 lchip, ctc_nh_nsh_param_t* nsh_info, DsNshEdit_m* ds, uint32 stats_ptr, uint32 ch_prof_id, uint8 op_type)
{
    uint8 ttl = 63;
    uint8 update_ttl_valid = 0;
    uint8 ttl_dec_valid = 0;
    uint8 si = 0;
    uint8 update_si_valid = 0;
    uint8 si_dec_valid = 0;
    uint8 next_protocol = 0;

    op_type = op_type? op_type: SYS_NSH_UPDATE;
    if ( (op_type == SYS_NSH_INSERT || op_type == SYS_NSH_UPDATE) && 0 != nsh_info->ttl)
    {
        ttl = nsh_info->ttl;
        update_ttl_valid = 1;
    }
    else if (!CTC_FLAG_ISSET(nsh_info->flags, CTC_NH_NSH_FLAG_TTL_NO_DEC))
    {
        update_ttl_valid = 1;
        ttl_dec_valid = 1;
    }
    if (0 != nsh_info->si)
    {
        si = nsh_info->si;
        update_si_valid = 1;
    }
    else if (!CTC_FLAG_ISSET(nsh_info->flags, CTC_NH_NSH_FLAG_SI_NO_DEC))
    {
        update_si_valid = 1;
        si_dec_valid = 1;
    }
    if (CTC_FLAG_ISSET(nsh_info->flags, CTC_NH_NSH_FLAG_VXLAN_RSV))
    {
        op_type = 0;
        ttl = 63;
        ttl_dec_valid = update_ttl_valid = 0;
        si = nsh_info->si;
        update_si_valid = 1;
        si_dec_valid = 0;
        ch_prof_id = SYS_NH_NSH_PROF_VXLAN_RSV;
        next_protocol = nsh_info->ch[0];
    }
    SetDsNshEdit(V, hashType_f, ds, SYS_SCL_HASH_TYPE_NSH);
    SetDsNshEdit(V, isNshEditDs_f, ds, 1);
    SetDsNshEdit(V, nshBhLength_f, ds, SYS_NSH_BH_LEN + (nsh_info->ch_len>>2));
    SetDsNshEdit(V, nshBhMdType_f, ds, (nsh_info->ch_len != CTC_NH_NSH_CH_LEN) ? 2: 1);
    SetDsNshEdit(V, nshBhNextProtocol_f, ds, next_protocol);
    SetDsNshEdit(V, nshNextProtocolAutoIdentifyEn_f, ds, 1);
    SetDsNshEdit(V, nshBhTtl_f, ds, ttl);
    SetDsNshEdit(V, updateNshTtl_f, ds, update_ttl_valid);
    SetDsNshEdit(V, nshTtlDecrease_f, ds, ttl_dec_valid);
    SetDsNshEdit(V, nshSphSi_f, ds, si);
    SetDsNshEdit(V, updateNshSi_f, ds, update_si_valid);
    SetDsNshEdit(V, nshSiDecrease_f, ds, si_dec_valid);
    SetDsNshEdit(V, updateNshSiMode_f, ds, si_dec_valid);
    SetDsNshEdit(V, nshSphSpi_f, ds, nsh_info->spi);
    SetDsNshEdit(V, updateNshSpi_f, ds, nsh_info->spi? 1: 0);
    SetDsNshEdit(V, valid_f, ds, 1);
    SetDsNshEdit(V, xKeyValid_f, ds, 1);
    SetDsNshEdit(V, nshOperationType_f, ds, op_type);
    SetDsNshEdit(V, nshUpdateOpMode_f, ds, 1);
    SetDsNshEdit(V, statsPtr_f, ds, stats_ptr);
    SetDsNshEdit(V, nshContextHdrTemplatePtr_f, ds, ch_prof_id);
}

void*
sys_at_nh_add_nsh_ch_profile(uint8 lchip, sys_nh_nsh_sp_ch_t* nsh_ch_profile, void* old_nsh_ch_profile)
{
    int32  ret = 0;
    sys_nh_nsh_sp_ch_t* out_nsh_ch = NULL;

    ret = ctc_spool_add(g_usw_nh_master[lchip]->nsh_ch_spool, nsh_ch_profile, old_nsh_ch_profile, &out_nsh_ch);
    if (ret != CTC_E_NONE)
    {
        ctc_spool_remove(g_usw_nh_master[lchip]->nsh_ch_spool, out_nsh_ch, NULL);
        return NULL;
    }
    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "Add NSH Context Header ProfileId: %d\n", out_nsh_ch->index.index);
    return out_nsh_ch;
}

int32
sys_at_nh_remove_nsh_ch_profile(uint8 lchip, sys_nh_nsh_sp_ch_t* p_nsh_ch)
{
    int32  ret = 0;

    if (p_nsh_ch)
    {
        ret = ctc_spool_remove(g_usw_nh_master[lchip]->nsh_ch_spool, p_nsh_ch, NULL);
    }
    return ret ;
}

STATIC INLINE int32
_sys_at_nh_build_nsh_edit_with_profile(uint8 lchip, uint8 op_type, ctc_nh_nsh_param_t* nsh_info,
                                        uint32 stats_ptr, sys_nh_db_nsh_t* p_nsh, sys_nh_nsh_sp_ch_t** new_ch)
{
    int32 ret = 0;
    sys_nh_nsh_sp_ch_t nsh_ch;
    DsNshEditContextHeaderProfile_m profile;
    DsNshEdit_m ds;
    uint32 cmd = 0;
    uint32 tbl_id = 0;
    uint8 prof_id = 0;

    sal_memset(&nsh_ch, 0, sizeof(sys_nh_nsh_sp_ch_t));
    if (0 != nsh_info->ch_len && !CTC_FLAG_ISSET(nsh_info->flags, CTC_NH_NSH_FLAG_VXLAN_RSV))
    {
        sal_memcpy(nsh_ch.ch, nsh_info->ch, nsh_info->ch_len);
        nsh_ch.ch_len = nsh_info->ch_len;
        *new_ch = sys_at_nh_add_nsh_ch_profile(lchip, &nsh_ch, p_nsh->ch);
        if (NULL == *new_ch)
        {
            return CTC_E_NO_RESOURCE;
        }
        prof_id = (*new_ch)->index.index;
        sal_memset(&profile, 0, sizeof(DsNshEditContextHeaderProfile_m));
        SYS_NH_NSH_SET_HW_CH(nsh_ch.ch);
        SetDsNshEditContextHeaderProfile(A, nshContextHeaderTemplate_f, &profile, nsh_ch.ch);
        cmd = DRV_IOW(DsNshEditContextHeaderProfile_t, DRV_ENTRY_FLAG);
        CTC_ERROR_GOTO(DRV_IOCTL(lchip, prof_id, cmd, &profile), ret, error_proc);
    }
    sal_memset(&ds, 0, sizeof(DsNshEdit_m));
    sys_at_nh_build_nsh_edit(lchip, nsh_info, &ds, stats_ptr, prof_id, op_type);

    SYS_NH_NSH_EDIT_TBL(lchip, tbl_id);
    cmd = DRV_IOW(tbl_id, DRV_ENTRY_FLAG);
    CTC_ERROR_GOTO(DRV_IOCTL(lchip, p_nsh->edit_offset, cmd, &ds), ret, error_proc);
    return CTC_E_NONE;
error_proc:
    if (0 != nsh_info->ch_len)
    {
        sys_at_nh_remove_nsh_ch_profile(lchip, *new_ch);
    }
    return ret;
}

int32
sys_at_nh_add_nsh_id(uint8 lchip, uint16 nsh_id, ctc_nh_nsh_param_t* nsh_info, uint32 stats_ptr, uint8 op_type)
{
    int32 ret = 0;
    sys_nh_db_nsh_t nsh_db;
    sys_nh_db_nsh_t* p_nsh = NULL;
    sys_usw_opf_t nsh_opf;
    uint32 temp_data = 0;
    uint32 entry_num = 0;

    if ( (CTC_WB_STATUS(lchip) == CTC_WB_STATUS_RELOADING))
    {
        return CTC_E_NONE;
    }

    nsh_db.nsh_id = nsh_id;
    p_nsh = ctc_hash_lookup(g_usw_nh_master[lchip]->nsh, &nsh_db);
    if (NULL != p_nsh)
    {
        return CTC_E_EXIST;
    }
    p_nsh = mem_malloc(MEM_NEXTHOP_MODULE, sizeof(sys_nh_db_nsh_t));
    if (NULL == p_nsh)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "malloc sys_nh_db_nsh_t fail!!!\n");
        return CTC_E_NO_MEMORY;
    }

    nsh_opf.reverse = 0;
    nsh_opf.multiple = 1;
    nsh_opf.pool_type = g_usw_nh_master[lchip]->nsh_opf_type;
    nsh_opf.pool_index = 0;
    if (CTC_FLAG_ISSET(nsh_info->flags, CTC_NH_NSH_FLAG_EDIT_OFFSET_VALID))
    {
        CTC_ERROR_GOTO(sys_usw_opf_alloc_offset_from_position(SYS_MAP_LCHIP(lchip, CTC_FEATURE_NEXTHOP), &nsh_opf, 1, nsh_info->edit_offset), ret, free_mem);
        p_nsh->edit_offset = nsh_info->edit_offset;
    }
    else
    {
        CTC_ERROR_GOTO(sys_usw_opf_alloc_offset(SYS_MAP_LCHIP(lchip, CTC_FEATURE_NEXTHOP), &nsh_opf, 1, &temp_data), ret, free_mem);
        p_nsh->edit_offset = temp_data;
    }
    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "nsh_edit_offset : %u\n", p_nsh->edit_offset);

    SYS_NH_NSH_EDIT_TBL(lchip, temp_data);
    CTC_ERROR_GOTO(sys_usw_ftm_query_table_entry_num(lchip, temp_data, &entry_num), ret, error0);
    if (8192 == entry_num && 4096 == sys_usw_ftm_get_spec(lchip,CTC_FTM_SPEC_NSH))
    {
        p_nsh->edit_offset = SYS_NH_NSH_EDIT_OFFSET_MAP(p_nsh->edit_offset);
    }

    p_nsh->ch = NULL;
    p_nsh->nsh_id = nsh_db.nsh_id;
    CTC_ERROR_GOTO(_sys_at_nh_build_nsh_edit_with_profile(lchip, op_type, nsh_info,
                                                           stats_ptr, p_nsh, &p_nsh->ch), ret, error0);
    if (!ctc_hash_insert(g_usw_nh_master[lchip]->nsh, p_nsh))
    {
        ret = CTC_E_NO_MEMORY;
        goto error1;
    }
    return CTC_E_NONE;
error1:
    if (p_nsh->ch)
    {
        sys_at_nh_remove_nsh_ch_profile(lchip, p_nsh->ch);
    }
error0:
    sys_usw_opf_free_offset(SYS_MAP_LCHIP(lchip, CTC_FEATURE_NEXTHOP), &nsh_opf, 1, p_nsh->edit_offset);
free_mem:
    mem_free(p_nsh);
    return ret;
}

int32
sys_at_nh_update_nsh_id(uint8 lchip, uint16 nsh_id, ctc_nh_nsh_param_t* nsh_info, uint32 stats_ptr)
{
    sys_nh_nsh_sp_ch_t* new_ch = NULL;
    sys_nh_db_nsh_t nsh_db;
    sys_nh_db_nsh_t* p_nsh = NULL;

    nsh_db.nsh_id = nsh_id;
    p_nsh = ctc_hash_lookup(g_usw_nh_master[lchip]->nsh, &nsh_db);
    if (NULL == p_nsh)
    {
        return CTC_E_NOT_EXIST;
    }

    CTC_ERROR_RETURN(_sys_at_nh_build_nsh_edit_with_profile(lchip, 0, nsh_info, stats_ptr, p_nsh, &new_ch));
    p_nsh->ch = new_ch;

    return CTC_E_NONE;
}

int32
sys_at_nh_remove_nsh_id(uint8 lchip, uint16 nsh_id)
{
    sys_nh_db_nsh_t nsh_db;
    sys_usw_opf_t nsh_opf;
    sys_nh_db_nsh_t* p_nsh = NULL;

    nsh_db.nsh_id = nsh_id;
    p_nsh = ctc_hash_lookup(g_usw_nh_master[lchip]->nsh, &nsh_db);
    if (NULL == p_nsh)
    {
        return CTC_E_NOT_EXIST;
    }
    if (p_nsh->ch)
    {
        sys_at_nh_remove_nsh_ch_profile(lchip, p_nsh->ch);
    }

    nsh_opf.multiple = 1;
    nsh_opf.reverse = 0;
    nsh_opf.pool_type = g_usw_nh_master[lchip]->nsh_opf_type;
    nsh_opf.pool_index = 0;
    sys_usw_opf_free_offset(SYS_MAP_LCHIP(lchip, CTC_FEATURE_NEXTHOP), &nsh_opf, 1, SYS_NH_NSH_EDIT_OFFSET_UNMAP(p_nsh->edit_offset));
    ctc_hash_remove(g_usw_nh_master[lchip]->nsh, p_nsh);
    mem_free(p_nsh);
    return CTC_E_NONE;
}

int32
sys_at_nh_nsh_init(uint8 lchip, uint8 is_deinit)
{
    sys_usw_opf_t nsh_opf;
    ctc_spool_t nsh_ch_spool;
    ctc_nh_nsh_param_t nsh_param;
    ds_t ds;
    int32 ret = CTC_E_NONE;
    uint32 cmd = 0;
    uint32 tbl_id = 0;
    uint32 entry_num = 0;
    uint32 vxlan_chain_mask[2] = {0xFFFFFF00,0};
    uint16 loop = 0;
    uint16 entry_index = 0;
    uint16 nsh_edit_num = 0;
    uint16 nsh_id = 0;
    drv_ftm_info_detail_t ftm_info;

    ftm_info.info_type = DRV_FTM_INFO_TYPE_MAX;
    CTC_ERROR_RETURN(drv_usw_ftm_get_info_detail(lchip, &ftm_info));
    if (!ftm_info.nsh_mode)
    {
        return CTC_E_NONE;
    }
    tbl_id = CTC_IS_BIT_SET(ftm_info.nsh_mode, 0) ? DsNshEdit1_t : DsNshEdit0_t;
    CTC_ERROR_RETURN(sys_usw_ftm_query_table_entry_num(lchip, tbl_id, &entry_num));
    g_usw_nh_master[lchip]->nsh_total_num = entry_num;
    if ((!CTC_WB_ENABLE(lchip) || CTC_WB_STATUS_RELOADING != CTC_WB_STATUS(lchip)) && (FALSE == is_deinit))
    {
        /*register init*/
        cmd = DRV_IOR(IpeLookupCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
        SetIpeLookupCtl(V, nshExtType_f, &ds, SYS_PARSER_EXT_TYPE_NSH);
        cmd = DRV_IOW(IpeLookupCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));

        cmd = DRV_IOR(EpeAclQosCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
        SetEpeAclQosCtl(V, nshExtType_f, &ds, SYS_PARSER_EXT_TYPE_NSH);
        cmd = DRV_IOW(EpeAclQosCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));

        cmd = DRV_IOR(EpeNextHopCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
        SetEpeNextHopCtl(V, nshExtType_f, &ds, SYS_PARSER_EXT_TYPE_NSH);
        //__TODO__SetEpeNextHopCtl(V, nshEditStatsBase_f, &ds, 0);
        cmd = DRV_IOW(EpeNextHopCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));

        cmd = DRV_IOR(IpePreLookupCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
        SetIpePreLookupCtl(V, nshTtlCheckThreshold_f, &ds, 1);
        SetIpePreLookupCtl(V, nshCheckEn_f, &ds, 0xDF);
        cmd = DRV_IOW(IpePreLookupCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));

        cmd = DRV_IOR(EpePktProcCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
        /*
        SetEpePktProcCtl(V, nshBhVersion_f, &ds, 0);
        SetEpePktProcCtl(V, nshBhObit_f, &ds, 0);
        SetEpePktProcCtl(V, nshSpiSiCheckThreshold_f, &ds, 0);
        */
        SetEpePktProcCtl(V, nshExtType_f, &ds, SYS_PARSER_EXT_TYPE_NSH);
        SetEpePktProcCtl(V, nshBhTtlCheckThreshold_f, &ds, 0);
        SetEpePktProcCtl(V, nshBhTtlCheckFailDiscardEn_f, &ds, 1);
        SetEpePktProcCtl(V, nshBhTtlCheckFailExceptionEn_f, &ds, 1);
        SetEpePktProcCtl(V, nshSpiSiCheckFailDiscardEn_f, &ds, 1);
        SetEpePktProcCtl(V, nshSpiSiCheckFailExceptionEn_f, &ds, 1);
        SetEpePktProcCtl(V, nshTtlCheck_f, &ds, 0x6);
        SetEpePktProcCtl(V, vxlanWithServiceChainProfile_f, &ds, SYS_NH_NSH_PROF_VXLAN_RSV);
        SetEpePktProcCtl(V, vxlanEditShareNshInfoEn_f, &ds, 1);
        SetEpePktProcCtl(A, serviceChainHdrMask_f, &ds, vxlan_chain_mask);
        cmd = DRV_IOW(EpePktProcCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));

        cmd = DRV_IOR(IpeFlowHashCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
        SetIpeFlowHashCtl(V, nshCheckDiscardEn_f, &ds, 0x7F);
        SetIpeFlowHashCtl(V, nshCheckExceptionEn_f, &ds, 0x7F);
        cmd = DRV_IOW(IpeFlowHashCtl_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));


        cmd = DRV_IOW(tbl_id, DRV_ENTRY_FLAG);
        sal_memset(&ds,0,sizeof(ds));
        SetDsNshEdit(V, hashType_f, ds, SYS_SCL_HASH_TYPE_NSH);
        SetDsNshEdit(V, valid_f, ds, 1);
        SetDsNshEdit(V, xKeyValid_f, ds, 1);
        switch(ftm_info.nsh_mode)
        {
        case SYS_NH_NSH_MODE_ESCL0_PART:
        case SYS_NH_NSH_MODE_ESCL1_PART:
            sys_usw_ftm_query_table_entry_num(lchip,tbl_id,&entry_num);
            nsh_edit_num = sys_usw_ftm_get_spec(lchip, CTC_FTM_SPEC_NSH);
            if (entry_num == 8192 && nsh_edit_num == 4096)
            /*escl = 8k, nsh edit = 4k*/
            {
                loop = 0;
                do {
                    entry_index = SYS_NH_NSH_EDIT_OFFSET_MAP(loop);
                    CTC_ERROR_RETURN(DRV_IOCTL(lchip,entry_index,cmd,&ds));
                }while(++loop<nsh_edit_num);
            }
            else
            /*escl > 8k, nsh edit = 1k/2k/3k/4k*/
            {
                loop = 0;
                do {
                    CTC_ERROR_RETURN(DRV_IOCTL(lchip,loop,cmd,&ds));
                }while(++loop<nsh_edit_num);
            }
            break;
        case SYS_NH_NSH_MODE_ESCL0:
        case SYS_NH_NSH_MODE_ESCL1:
            /*no break, escl > 8k, nsh edit = 8k*/
        case SYS_NH_NSH_MODE_NONE:
        default:
            break;
        }
    }
    if (FALSE == is_deinit)
    {
        nsh_edit_num = sys_usw_ftm_get_spec(lchip, CTC_FTM_SPEC_NSH);
        g_usw_nh_master[lchip]->nsh_edit_num = nsh_edit_num;
        /*nsh hash init*/
        g_usw_nh_master[lchip]->nsh = ctc_hash_create(nsh_edit_num/SYS_NSH_HASH_BLOCK_SIZE, SYS_NSH_HASH_BLOCK_SIZE,
                        (hash_key_fn)_sys_at_nh_nsh_hash_make,
                        (hash_cmp_fn)_sys_at_nh_nsh_hash_cmp);
        if (NULL == g_usw_nh_master[lchip]->nsh)
        {
            return CTC_E_INIT_FAIL;
        }
        /*nsh opf init*/
        CTC_ERROR_GOTO(sys_usw_opf_init(lchip, &g_usw_nh_master[lchip]->nsh_opf_type, 1, "opf-type-nsh-id"), ret, error1);
        nsh_opf.multiple = 1;
        nsh_opf.reverse = 0;
        nsh_opf.pool_index = 0;
        nsh_opf.pool_type  = g_usw_nh_master[lchip]->nsh_opf_type;
        CTC_ERROR_GOTO(sys_usw_opf_init_offset(lchip, &nsh_opf, 0, nsh_edit_num), ret, error2);

        /*context header spool init*/
        CTC_ERROR_GOTO(sys_usw_ftm_query_table_entry_num(lchip, DsNshEditContextHeaderProfile_t, &entry_num), ret, error2);
        sal_memset(&nsh_ch_spool, 0, sizeof(ctc_spool_t));
        nsh_ch_spool.lchip = lchip;
        nsh_ch_spool.block_num = 4;
        nsh_ch_spool.block_size = entry_num/nsh_ch_spool.block_num;
        nsh_ch_spool.max_count = entry_num;
        nsh_ch_spool.user_data_size = sizeof(sys_nh_nsh_sp_ch_t);
        nsh_ch_spool.desc = "opf-nsh-context-header";
        nsh_ch_spool.key_size = CTC_OFFSET_OF(sys_nh_nsh_sp_ch_t, calc_key_len);
        nsh_ch_spool.min_index = SYS_NH_NSH_PROF_USER;
        nsh_ch_spool.max_index = entry_num - 1;
        g_usw_nh_master[lchip]->nsh_ch_spool = ctc_spool_create(&nsh_ch_spool);
        if (!g_usw_nh_master[lchip]->nsh_ch_spool)
        {
            ret = CTC_E_INIT_FAIL;
            goto error2;
        }

        sal_memset(&nsh_param, 0, sizeof(ctc_nh_nsh_param_t));
        CTC_SET_FLAG(nsh_param.flags, CTC_NH_NSH_FLAG_SI_NO_DEC);
        nsh_id = SYS_NH_NSH_UPDATE_ID;
        CTC_ERROR_GOTO(sys_at_nh_add_nsh_id(lchip, nsh_id, &nsh_param, 0, SYS_NSH_UPDATE), ret, error2);
        nsh_id = SYS_NH_NSH_REMOVE_ID;
        CTC_ERROR_GOTO(sys_at_nh_add_nsh_id(lchip, nsh_id, &nsh_param, 0, SYS_NSH_REMOVE), ret, error2);

        if (!(CTC_WB_ENABLE(lchip) && CTC_WB_STATUS(lchip) == CTC_WB_STATUS_RELOADING))
        {
            if (MCHIP_XDATA(lchip)->func_en && !SYS_VCHIP_PP_IS_NOT_BASE(lchip))
            {
                CTC_ERROR_GOTO(MCHIP_XDATA(lchip)->func_en(lchip, SYS_XDATA_FUNC_NSH, 1, (void*)(&g_usw_nh_master[lchip]->nsh_total_num)), ret, error2);
            }
            CTC_ERROR_RETURN(sys_usw_nh_offset_alloc(lchip, SYS_NH_ENTRY_TYPE_L3EDIT_ADD_RAW, 1, &g_usw_nh_master[lchip]->rsv_l3edit_for_nsh));
            CTC_ERROR_RETURN(sys_usw_nh_write_asic_table(lchip, SYS_NH_ENTRY_TYPE_L3EDIT_ADD_RAW, g_usw_nh_master[lchip]->rsv_l3edit_for_nsh, NULL));
        }
    }
    else
    {
        sys_at_nh_remove_nsh_id(lchip, SYS_NH_NSH_UPDATE_ID);
        sys_at_nh_remove_nsh_id(lchip, SYS_NH_NSH_REMOVE_ID);
        if (g_usw_nh_master[lchip]->nsh_ch_spool)
        {
            ctc_spool_free(g_usw_nh_master[lchip]->nsh_ch_spool);
        }
        /* nh_opf_type destroyed in sys_usw_nh_deinit() */
        sys_usw_opf_deinit(lchip, g_usw_nh_master[lchip]->nsh_opf_type);
        ctc_hash_free2(g_usw_nh_master[lchip]->nsh, (hash_traversal_fn)_sys_at_nh_nsh_free_node, NULL);
    }

    return CTC_E_NONE;
error2:
    sys_usw_opf_deinit(lchip, g_usw_nh_master[lchip]->nsh_opf_type);
error1:
    ctc_hash_free(g_usw_nh_master[lchip]->nsh);
    return ret;
}

int32
sys_at_nh_get_nsh_edit_info(uint8 lchip, uint16 nsh_id, sys_nh_nsh_edit_info_t* p_edit)
{
    sys_nh_db_nsh_t nsh_db;
    sys_nh_db_nsh_t* p_nsh = NULL;

    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_PARAM, "nsh_id : %u\n", nsh_id);

    nsh_db.nsh_id = nsh_id;
    p_nsh = ctc_hash_lookup(g_usw_nh_master[lchip]->nsh, &nsh_db);
    if (NULL == p_nsh)
    {
        return CTC_E_NOT_EXIST;
    }
    if (p_nsh->edit_offset & 1)
    {
        p_edit->bus_offset = p_nsh->edit_offset;
    }
    else
    {
        p_edit->bus_offset = p_nsh->edit_offset + g_usw_nh_master[lchip]->nsh_total_num;
    }
    return CTC_E_NONE;
}

#if 0
int32
sys_at_nh_get_nsh_edit(uint8 lchip, uint16 nsh_id, ctc_nh_nsh_param_t* nsh_info)
{
    uint32 cmd = 0;
    uint32 tbl_id = 0;
    DsNshEdit_m nsh_edit;
    sys_nh_db_nsh_t nsh_db;
    sys_nh_db_nsh_t* p_nsh = NULL;

    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_PARAM, "nsh_id : %u\n", nsh_id);
    nsh_db.nsh_id = nsh_id;
    p_nsh = ctc_hash_lookup(g_usw_nh_master[lchip]->nsh, &nsh_db);
    if (NULL == p_nsh)
    {
        return CTC_E_NOT_EXIST;
    }
    SYS_NH_NSH_EDIT_TBL(lchip, tbl_id);
    cmd = DRV_IOR(tbl_id, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, p_nsh->edit_offset, cmd, &nsh_edit));
    nsh_info->edit_offset = SYS_NH_NSH_EDIT_OFFSET_UNMAP(p_nsh->edit_offset);
    if (GetDsNshEdit(V, nshTtlDecrease_f, &nsh_edit))
    {
        CTC_SET_FLAG(nsh_info->flags, CTC_NH_NSH_FLAG_TTL_NO_DEC);
    }
    if (GetDsNshEdit(V, nshSiDecrease_f, &nsh_edit))
    {
        CTC_SET_FLAG(nsh_info->flags, CTC_NH_NSH_FLAG_SI_NO_DEC);
    }
    if (!GetDsNshEdit(V, nshOperationType_f, &nsh_edit))
    {
        CTC_SET_FLAG(nsh_info->flags, CTC_NH_NSH_FLAG_VXLAN_RSV);
        nsh_info->ch[0] = GetDsNshEdit(V, nshBhNextProtocol_f, &nsh_edit);
    }
    if (p_nsh->ch)
    {
        nsh_info->ch_len = p_nsh->ch->ch_len;
        sal_memcpy(nsh_info->ch, p_nsh->ch->ch, nsh_info->ch_len);
    }
    nsh_info->ttl = GetDsNshEdit(V, nshBhTtl_f, &nsh_edit);
    nsh_info->si = GetDsNshEdit(V, nshSphSi_f, &nsh_edit);
    nsh_info->spi = GetDsNshEdit(V, nshSphSpi_f, &nsh_edit);
    return CTC_E_NONE;
}
#endif

STATIC int32
_sys_usw_nh_wb_mapping_nsh(uint8 lchip, sys_nh_db_nsh_t* p_db, sys_wb_nh_nsh_t *p_wb, uint8 sync)
{
    sys_usw_opf_t opf;
    uint32 cmd = 0;
    uint32 tbl_id = 0;
    DsNshEdit_m ds;
    DsNshEditContextHeaderProfile_m profile;
    sys_nh_nsh_sp_ch_t nsh_ch;
    uint8 ch_prof_id = 0;
    if (sync)
    {
        p_wb->nsh_id       =  p_db->nsh_id;
        p_wb->edit_offset  =  p_db->edit_offset;
        p_wb->nsh_ch_len   = p_db->ch ? p_db->ch->ch_len : 0;
    }
    else
    {
        p_db->nsh_id        =  p_wb->nsh_id ;
        p_db->edit_offset   =  p_wb->edit_offset;

        sal_memset(&opf, 0, sizeof(sys_usw_opf_t));
        opf.pool_type = g_usw_nh_master[lchip]->nsh_opf_type;
        opf.pool_index = 0;
        sys_usw_opf_alloc_offset_from_position( SYS_MAP_LCHIP(lchip, CTC_FEATURE_NEXTHOP), &opf, 1, p_db->edit_offset);
        SYS_NH_NSH_EDIT_TBL(lchip, tbl_id);
        cmd = DRV_IOR(tbl_id, DRV_ENTRY_FLAG);
        DRV_IOCTL(lchip, p_db->edit_offset, cmd, &ds);
        ch_prof_id = GetDsNshEdit(V, nshContextHdrTemplatePtr_f, &ds);
        if (0 != ch_prof_id && 0 != p_wb->nsh_ch_len)
        {
            sal_memset(&nsh_ch, 0, sizeof(sys_nh_nsh_sp_ch_t));
            cmd = DRV_IOR(DsNshEditContextHeaderProfile_t, DRV_ENTRY_FLAG);
            DRV_IOCTL(lchip, ch_prof_id, cmd, &profile);
            GetDsNshEditContextHeaderProfile(A, nshContextHeaderTemplate_f, &profile, nsh_ch.ch);
            SYS_NH_NSH_SET_HW_CH(nsh_ch.ch);
            nsh_ch.ch_len = p_wb->nsh_ch_len;
            nsh_ch.index.index = ch_prof_id;
            p_db->ch = sys_at_nh_add_nsh_ch_profile(lchip, &nsh_ch, NULL);
        }
    }
    return CTC_E_NONE;
}


STATIC int32
_sys_at_nh_wb_restore_nsh(uint8 lchip, ctc_wb_query_t *p_wb_query)
{
    sys_nh_db_nsh_t* p_db = NULL;
    sys_wb_nh_nsh_t wb;
    sys_wb_nh_nsh_t *p_wb = &wb;
    uint32 entry_cnt = 0;
    int32 ret = 0;
    CTC_WB_INIT_QUERY_T(p_wb_query, sys_wb_nh_nsh_t, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_NSH);
    /* set default value for new added fields, the default value may not be zeros */
    sal_memset(&wb, 0, sizeof(sys_wb_nh_nsh_t));
    CTC_WB_QUERY_ENTRY_BEGIN(p_wb_query);
    sal_memcpy((uint8 *)p_wb, (uint8 *)p_wb_query->buffer + entry_cnt * (p_wb_query->key_len + p_wb_query->data_len),
               (p_wb_query->key_len + p_wb_query->data_len));
    entry_cnt++;
    p_db  = mem_malloc(MEM_NEXTHOP_MODULE, sizeof(sys_nh_db_nsh_t));
    if (NULL == p_db)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory \n");
        return CTC_E_NO_MEMORY;
    }
    sal_memset(p_db, 0, sizeof(sys_nh_db_nsh_t));
    _sys_usw_nh_wb_mapping_nsh(lchip, p_db, p_wb, 0);
    if (NULL == ctc_hash_insert(g_usw_nh_master[lchip]->nsh, p_db))
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No Memory \n");
        mem_free(p_db);
        return CTC_E_NO_MEMORY;
    }
    CTC_WB_QUERY_ENTRY_END(p_wb_query);
    done:
    return ret;
}

STATIC int32
_sys_usw_nh_wb_traverse_sync_nsh(void* bucket_data, void* user_data)
{
    int32 ret;
    sys_traverse_t *wb_traverse = (sys_traverse_t*)user_data;
    sys_nh_db_nsh_t *p_db = (sys_nh_db_nsh_t*)bucket_data;
    ctc_wb_data_t *wb_data = wb_traverse->data;
    sys_wb_nh_nsh_t   *p_wb;
    uint8 lchip = 0;
    uint32 max_buffer_cnt = wb_data->buffer_len / (wb_data->key_len + wb_data->data_len);

    p_wb = (sys_wb_nh_nsh_t *)wb_data->buffer + wb_data->valid_cnt;
    lchip = wb_traverse->value1;

    _sys_usw_nh_wb_mapping_nsh(lchip, p_db,p_wb,1);

    if (++wb_data->valid_cnt ==  max_buffer_cnt)
    {
        ret = ctc_wb_add_entry(wb_data);
        if ( ret != CTC_E_NONE )
        {
           return ret;
        }
        wb_data->valid_cnt = 0;
    }

    return CTC_E_NONE;
}

#define ____NSH_END____

int32
sys_at_nh_wb_restore(uint8 lchip, ctc_wb_query_t *p_wb_query)
{
    CTC_ERROR_RETURN(_sys_at_nh_wb_restore_nsh(lchip, p_wb_query));
    return CTC_E_NONE;
}

int32
sys_at_nh_wb_sync(uint8 lchip, uint32 app_id, ctc_wb_data_t* p_wb_data)
{
    sys_traverse_t  wb_traverse;
    if ((app_id == 0 || CTC_WB_SUBID(app_id) == SYS_WB_APPID_NH_SUBID_NSH))
    {
        /*sync nsh*/
        CTC_WB_INIT_DATA_T(p_wb_data, sys_wb_nh_nsh_t, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_NSH);
        sal_memset(&wb_traverse, 0, sizeof(wb_traverse));
        wb_traverse.data = p_wb_data;
        wb_traverse.value1 = lchip;
        ctc_hash_traverse(g_usw_nh_master[lchip]->nsh, _sys_usw_nh_wb_traverse_sync_nsh, &wb_traverse);
        if (p_wb_data->valid_cnt > 0)
        {
            CTC_ERROR_RETURN(ctc_wb_add_entry(p_wb_data));
            p_wb_data->valid_cnt = 0;
        }
        CTC_WB_SYNC_END_DATA(CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_NSH);
    }
    return CTC_E_NONE;
}

#endif

